#include "Entorno.h"

Entorno* Entorno::instance = NULL;

Entorno::Entorno() : semaforo((char*)SEM_NAME, ID_SEM_USRS,0), semaforoDescarga((char*)SEM_NAME, ID_SEM_DESC,0) {
	Log::getInstance()->log("Creando entorno.");
	int salida = bufferUsuarios.crear ( (char*)MEM_NAME,ID_MEM_USRS);
	if (salida == SHM_OK) {
		if(bufferUsuarios.cantidadProcesosAdosados() == 1){
			mem_usuarios users;
			for(int i = 0; i < MAX_USERS; i++) {
				users.usuarios[i].id = -1;
				strcpy(users.usuarios[i].nombre,"\0");
			}

			//semaforo.p();
			semaforo.inicializar();
			bufferUsuarios.escribir(users);
			semaforo.v();
		}
	} else {
		kill(getpid(),INTERRUPT_SIGNAL);
	}

	salida = solicitudesDescarga.crear ( (char*)MEM_NAME,ID_MEM_DESC);
	if (salida == SHM_OK) {
		if(solicitudesDescarga.cantidadProcesosAdosados() == 1){
			mem_descargas descargas;
			for(int i = 0; i < MAX_DESCARGAS; i++) {
				descargas.descargas[i].idUsuarioComparte = -1;
				descargas.descargas[i].idUsuarioDescarga = -1;
				strcpy(descargas.descargas[i].idDescarga,"\0");
				strcpy(descargas.descargas[i].pathArchivo,"\0");
			}

			//semaforo.p();
			semaforoDescarga.inicializar();
			solicitudesDescarga.escribir(descargas);
			semaforoDescarga.v();
		}
	} else {
		kill(getpid(),INTERRUPT_SIGNAL);
	}
	Log::getInstance()->log("Entorno creado.");
}

Entorno::~Entorno() {
	Log::getInstance()->log("Destruyendo entorno.");
	bufferUsuarios.liberar();
	if(bufferUsuarios.cantidadProcesosAdosados() == 0){
		semaforo.eliminar();
	}

	solicitudesDescarga.liberar();
	if (solicitudesDescarga.cantidadProcesosAdosados() == 0) {
		semaforoDescarga.eliminar();
	}
	Log::getInstance()->log("Entorno destruido.");
}

Entorno* Entorno::getInstance() {
	if (instance == NULL) {
		instance = new Entorno();
	}
	return instance;
}

void Entorno::destroy() {
	if (instance != NULL) {
		delete instance;
		instance = NULL;
	}
}

int Entorno::agregarUsuario(Usuario* us){
	stringstream logstream;
	usuario = us;
	logstream << "Agregando usuario\"" << us->getNombre() << "\" PID: "<< us->getId() << " al entorno";
	Log::getInstance()->log(logstream.str());

	//pido acceso a la memoria
	semaforo.p();
	mem_usuarios users;

	//leo lo que esta compartido
	users = bufferUsuarios.leer();

	bool encontrado = false;
	//en el primer slot vacio guardo al usuario
	for (int i = 0; i < MAX_USERS && !encontrado; i++) {
		if (users.usuarios[i].id == -1) {
			users.usuarios[i].id = us->getId();
			strcpy(users.usuarios[i].nombre,us->getNombre().c_str());
			//guardo memoria
			bufferUsuarios.escribir(users);
			encontrado = true;
		}
	}
	semaforo.v();

	if (encontrado) {
		stringstream logEncontrado;
		logEncontrado << "Se agrego al usuario \"" << us->getNombre() << "\" PID: " << us->getId() << " al entorno";
		Log::getInstance()->log(logEncontrado.str());
		return 0;
	} else {
		stringstream logEncontrado;
		logEncontrado << "No se agrego al usuario \"" << us->getNombre() << "\" PID: " << us->getId() << " al entorno";
		Log::getInstance()->log(logEncontrado.str());
		return 1;
	}
}

int Entorno::eliminarUsuario(){
	stringstream logstream;
	logstream << "Eliminando al usuario \"" << usuario->getNombre() << "\" PID: " << usuario->getId() << " al entorno";
	Log::getInstance()->log(logstream.str());
	semaforo.p();
	mem_usuarios users;
	users = bufferUsuarios.leer();
	bool eliminado = false;
	for (int i = 0; i < MAX_USERS && !eliminado; i++) {
		if (users.usuarios[i].id == usuario->getId()) {
			users.usuarios[i].id = -1;
			strcpy(users.usuarios[i].nombre,"\0");
			bufferUsuarios.escribir(users);
			eliminado = true;
		}
	}
	semaforo.v();

	if (eliminado) {
		stringstream logEliminado;
		logEliminado << "Se elimino al usuario \"" << usuario->getNombre() << "\" PID: " << usuario->getId() << " al entorno";
		Log::getInstance()->log(logEliminado.str());
		return 0;
	} else {
		stringstream logEliminado;
		logEliminado << "No se elimino al usuario \"" << usuario->getNombre() << "\" PID: " << usuario->getId() << " al entorno";
		Log::getInstance()->log(logEliminado.str());
		return 1;
	}
}

UsersMapType Entorno::obtenerUsuarios(){
	semaforo.p();
	mem_usuarios users;
	UsersMapType usersToReturn;
	users = bufferUsuarios.leer();
	for (int i = 0; i < MAX_USERS; i++) {
		if (users.usuarios[i].id != -1) {
			usersToReturn[users.usuarios[i].id] = users.usuarios[i].nombre;
		}
	}
	semaforo.v();

	return usersToReturn;
}

void Entorno::solicitarDescarga(SolicitudDescargaType sd){
	stringstream logstream;
	logstream << "Registrando solicitud de descarga. Uploader: \"" << sd.idUsuarioComparte << "\" Downloader: "<< sd.idUsuarioDescarga << " idDescarga: " << "";
	Log::getInstance()->log(logstream.str());

	//pido acceso a la memoria
	semaforoDescarga.p();
	mem_descargas solicitudes;

	//leo lo que esta compartido
	solicitudes = solicitudesDescarga.leer();

	bool encontrado = false;
	//en el primer slot vacio guardo la solicitud
	for (int i = 0; i < MAX_DESCARGAS && !encontrado; i++) {
		if (solicitudes.descargas[i].idUsuarioComparte == -1) {
			solicitudes.descargas[i] = sd;
			//guardo memoria
			solicitudesDescarga.escribir(solicitudes);
			encontrado = true;
		}
	}
	semaforoDescarga.v();

	if (!encontrado) {
		stringstream sstream;
		sstream << "Máxima cantidad de descargas simultáneas excedida. Solicitante: " << sd.idUsuarioDescarga << ";  Solicitado: " << sd.idUsuarioComparte << ";  Id solicitud: " << sd.idDescarga;
		Log::getInstance()->log(sstream.str());
		throw new DescargaFallidaException((char*)sstream.str().c_str());

	}
	stringstream logstream2;
	logstream2 << "Registrando solicitud de descarga. Uploader: \"" << sd.idUsuarioComparte << "\" Downloader: "<< sd.idUsuarioDescarga << " idDescarga: " << "";
	Log::getInstance()->log(logstream2.str());
}

SolicitudDescargaType Entorno::consultarSolicitudDescargaPara(int usuarioId){
	semaforoDescarga.p();
	mem_descargas solicitudes;
	SolicitudDescargaType solicitud;
	solicitudes = solicitudesDescarga.leer();
	for (int i = 0; i < MAX_DESCARGAS; i++) {
		if (solicitudes.descargas[i].idUsuarioComparte == usuarioId) {
			solicitud = solicitudes.descargas[i];

			solicitudes.descargas[i].idUsuarioComparte = -1;
			solicitudes.descargas[i].idUsuarioDescarga = -1;
			strcpy(solicitudes.descargas[i].idDescarga,"\0");
			strcpy(solicitudes.descargas[i].pathArchivo,"\0");
		}
	}
	semaforoDescarga.v();

	return solicitud;
}

SolicitudDescargaType Entorno::consultarSolicitudPublicarPara(int usuarioId) {
	semaforoDescarga.p();
	mem_descargas solicitudes;
	SolicitudDescargaType solicitud;
	solicitudes = solicitudesDescarga.leer();
	for (int i = 0; i < MAX_DESCARGAS; i++) {
		if (solicitudes.descargas[i].idUsuarioComparte == usuarioId && solicitudes.descargas[i].pathArchivo[0] == '\0') {
			solicitud = solicitudes.descargas[i];

			solicitudes.descargas[i].idUsuarioComparte = -1;
			solicitudes.descargas[i].idUsuarioDescarga = -1;
			strcpy(solicitudes.descargas[i].idDescarga,"\0");
			strcpy(solicitudes.descargas[i].pathArchivo,"\0");
		}
	}
	semaforoDescarga.v();

	return solicitud;
}

